/**
 * Copyright (c) 2016-2019 人人开源 All rights reserved.
 *
 * https://www.renren.io
 *
 * 版权所有，侵权必究！
 */

package edu.cug.water.crawler.job.utils;


import edu.cug.water.crawler.common.constant.Constant;
import edu.cug.water.crawler.common.utils.SpringContextUtils;
import edu.cug.water.crawler.config.NoticeConfig;
import edu.cug.water.crawler.job.entity.ScheduleJobEntity;
import edu.cug.water.crawler.job.entity.ScheduleJobLogEntity;
import edu.cug.water.crawler.job.service.ScheduleJobLogService;
import org.apache.commons.lang.StringUtils;
import org.quartz.*;
import org.quartz.spi.OperableTrigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.lang.reflect.Method;
import java.util.Date;


/**
 * 定时任务
 *
 * @author Mark sunlightcs@gmail.com
 */
public class ScheduleJob extends QuartzJobBean {
	private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap()
        		.get(ScheduleJobEntity.JOB_PARAM_KEY);

		//获取spring bean
        ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringContextUtils.getBean("scheduleJobLogService");
        
        //数据库保存执行记录
        ScheduleJobLogEntity log = new ScheduleJobLogEntity();
        log.setJobName(scheduleJob.getJobName());
        log.setJobId(scheduleJob.getJobId());
        log.setBeanName(scheduleJob.getBeanName());
        log.setParams(scheduleJob.getParams());
        log.setCreateTime(new Date());
		//任务开始时间
		long startTime = System.currentTimeMillis();

		final JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
		String key = Constant.ScheduleRetry.COUNT_MAP_KEY.name();
		final int retries = jobDataMap.containsKey(key) ? jobDataMap.getIntValue(key) : 0;
		try {
			//执行任务
			logger.debug("任务准备执行，任务ID：" + scheduleJob.getJobId());

			Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());
			Method method = target.getClass().getDeclaredMethod("run", String.class);
			int count = (Integer)method.invoke(target, scheduleJob.getParams());

			//任务执行总时长
			long times = System.currentTimeMillis() - startTime;
			log.setTimes((int) times);
			//任务状态    0：成功    1：失败
			log.setStatus(0);
			log.setDataCnt(count);
			log.setIsProcessed(1);
			logger.debug("任务执行完毕，任务ID：" + scheduleJob.getJobId() + "  总共耗时：" + times + "毫秒");

		} catch (Exception e) {
			logger.error("任务执行失败，任务ID：" + scheduleJob.getJobId(), e);
			Throwable cause = e.getCause();
			String message = null;
			if (!StringUtils.isEmpty(cause.getMessage()))
				message = cause.getMessage();
			else
				message = cause.getCause().toString();

			// retryJob
			retryJob(context);

			//任务执行总时长
			long times = System.currentTimeMillis() - startTime;
			log.setTimes((int) times);
			//任务状态    0：成功    1：失败
			log.setStatus(1);
			logger.error("第几次retry。 {}", retries);
			log.setIsProcessed(-retries);
			log.setError(StringUtils.substring(message, 0, 2000));
			log.setDataCnt(-1);
		} finally {
			scheduleJobLogService.save(log);
		}
    }

    void retryJob (JobExecutionContext context) throws JobExecutionException {

		final JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
		String key = Constant.ScheduleRetry.COUNT_MAP_KEY.name();
		final int retries = jobDataMap.containsKey(key) ? jobDataMap.getIntValue(key) : 0;

		if (retries < Constant.ScheduleRetry.MAX_RETRIES.getValue()) {

			// count retry
			jobDataMap.put(key, retries + 1);

			final JobDetail job = context
					.getJobDetail()
					.getJobBuilder()
					// to track the number of retries
					.withIdentity(context.getJobDetail().getKey().getName() + " - " + retries, "FailingJobsGroup")
					.usingJobData(jobDataMap)
					.build();

			final OperableTrigger trigger = (OperableTrigger) TriggerBuilder
					.newTrigger()
					.forJob(job)
					// trying to reduce back pressure, you can use another algorithm
					.startAt(new Date(context.getFireTime().getTime() + (retries * 1000)))
					.build();
			try {
				context.getScheduler().scheduleJob(job, trigger);
			} catch (SchedulerException e) {
				logger.error("Error creating job");
				throw new JobExecutionException(e);
			}
		} else {
			NoticeConfig notice = (NoticeConfig) SpringContextUtils.getBean("notice");
			boolean b = notice.sendQQMsg("Damn! Task Failed！");
			if (!b) logger.error("sendQQMsg Failed;");
			else logger.info("任务失败，发送QQ通知！");
			logger.error("任务重试次数达到最大重试次数！");
		}
	}
}
